package com.hishop.common.task;

import com.hishop.common.Constant;
import com.hishop.common.RedissonManager;
import com.hishop.service.IOrderService;
import com.hishop.util.PropertiesUtil;
import com.hishop.util.RedisShardedPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;

/**
 * 超过30分钟未付款的订单将其关闭
 * 注意：集群环境下每个应用都在执行定时任务！！！应该避免
 * Created by hejie on 2018/3/20.
 */
@Component
@Slf4j
public class CloseOrderTask {
    @Autowired
    private IOrderService iOrderService;
    @Autowired
    private RedissonManager redissonManager;

    //在服务器shutdown()之前进行的操作  v2版本
    @PreDestroy
    public void delLock() {
        RedisShardedPoolUtil.del(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
    }

    //@Scheduled(cron = "0 */1 * * * ?")
    public void closeOrderTaskV1() {
        log.info("关闭订单定时任务启动");
        int minute = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.minute"));
        iOrderService.closeOrder(minute);
        log.info("关闭订单定时任务结束");
    }

    //@Scheduled(cron = "0 */1 * * * ?")
    public void closeOrderTaskV2() {
        log.info("关闭订单定时任务启动");
        long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("redis.timeout"));
        Long setnxRet = RedisShardedPoolUtil.setnx(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeout));
        /***********程序走到这一步关闭了tomcat的话，这是就发生死锁了***********/
        if (setnxRet == null || setnxRet == 0) {
            log.info("没有获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
            log.info("关闭订单定时任务结束");
            return;
        }
        log.info("成功获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
        closeOrder(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
        log.info("关闭订单定时任务结束");
    }

    //@Scheduled(cron = "0 */1 * * * ?")
    public void closeOrderTaskV3() {
        log.info("关闭订单定时任务启动");
        long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("redis.timeout"));
        Long setnxRet = RedisShardedPoolUtil.setnx(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeout));
        /***********程序走到这一步关闭了tomcat的话，这是就发生死锁了***********/
        if (setnxRet == null || setnxRet == 0) {
            // log.info("没有获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
            //未获取到锁，继续判断，判断时间戳，看是否可以重置并获取到锁
            String lockValueStr = RedisShardedPoolUtil.get(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
            //是否超时
            if (lockValueStr != null && System.currentTimeMillis() > Long.valueOf(lockValueStr)) {
                String preLockValueStr = RedisShardedPoolUtil.getSet(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeout));
                //第一种情况：key失效代表当前没有任务可以获得锁；
                //第二种情况：当前获取的时间戳和上面第一次获得的时间戳一样表示可以重置并获得锁
                if (preLockValueStr == null || StringUtils.equals(preLockValueStr, lockValueStr)) {
                    log.info("成功获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
                    closeOrder(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
                } else
                    log.info("没有获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
            } else log.info("没有获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
            log.info("关闭订单定时任务结束");
            return;
        }
        log.info("成功获得分布式锁：{}", Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
        closeOrder(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
        log.info("关闭订单定时任务结束");
    }

    @Scheduled(cron = "0 */1 * * * ?")
    public void closeOrderTaskV4() {
        RLock rLock = redissonManager.getRedisson().getLock(Constant.RedisLock.CLOSE_ORDER_TASK_LOCK);
        boolean getLock = false;
        try {
            getLock = rLock.tryLock(0, 5, TimeUnit.SECONDS);
            if (getLock) {
                log.info("Redision获取分布式锁,ThreadName:{}", Thread.currentThread().getName());
                int minute = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.minute"));
                iOrderService.closeOrder(minute);
            } else {
                log.info("Redision没有获取分布式锁,ThreadName:{}", Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            log.error("Redision分布式锁获取异常", e);
        } finally {
            if (!getLock) {
                return;
            }
            rLock.unlock();//获取分布式锁用完之后，释放锁
            log.info("Redision分布式锁释放锁");
        }
    }

    private void closeOrder(String lockName) {
        RedisShardedPoolUtil.expire(lockName, 20);//有效期20秒，防止死锁
        log.info("获取{}，线程名:{}", lockName, Thread.currentThread().getName());
        int minute = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.minute"));
        // iOrderService.closeOrder(minute);
        RedisShardedPoolUtil.del(lockName);
        log.info("释放{}，线程名:{}", lockName, Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        String a = "";
        String b = null;
        System.out.println(StringUtils.equals(a, b));
    }
}
